package com.mallcai.bigdata.ladon.service.report.impl.handler;

import com.alibaba.fastjson.JSON;
import com.alicp.jetcache.Cache;
import com.alicp.jetcache.anno.CacheType;
import com.alicp.jetcache.anno.CreateCache;
import com.mallcai.bigdata.ladon.client.vo.ReportDataVO;
import com.mallcai.bigdata.ladon.datasource.RunInfoResult;
import com.mallcai.bigdata.ladon.dpl.entity.report.Report;
import com.mallcai.bigdata.ladon.dpl.entity.report.ReportSql;
import com.mallcai.bigdata.ladon.service.report.impl.ReportQueryContext;
import com.mallcai.bigdata.ladon.tools.MD5Util;
import lombok.extern.slf4j.Slf4j;
import org.springframework.core.annotation.Order;
import org.springframework.stereotype.Component;

import java.util.Map;
import java.util.concurrent.TimeUnit;

import static com.mallcai.bigdata.ladon.service.report.impl.ReportQueryConstants.KEY_IS_CACHE_RESULT;
import static com.mallcai.bigdata.ladon.service.report.impl.ReportQueryConstants.KEY_IS_FLOW_OFF;
import static com.mallcai.bigdata.ladon.service.report.impl.ReportQueryConstants.KEY_IS_STATIC_REPORT;
import static com.mallcai.bigdata.ladon.service.report.impl.ReportQueryConstants.KEY_RESULT_CACHE_KEY;

/**
 * @author feixy
 * @version 1.0
 * @date 2019-07-19 21:00
 */
@Slf4j
@Component
@Order(2500)
public class ReportQueryCacheHandler extends AbstractReportQueryBothHandler {

    @CreateCache(name = "report.reportQueryCache", cacheType = CacheType.REMOTE)
    private Cache<String, RunInfoResult<ReportDataVO>> cache;


    @Override
    public void preInternal(ReportQueryContext context) {
        ReportSql reportSql = context.getReportSql();
        Report report = context.getReport();
        int expireTime = getCacheExpire(context, reportSql);
        if(expireTime > 0){
           String cacheKey = createCacheKey(context.getReportCode(),reportSql.getText(), context.getOriginalParams());
            context.putInnerParam(KEY_RESULT_CACHE_KEY, cacheKey);
            RunInfoResult<ReportDataVO> runInfoResult = cache.get(cacheKey);
            if (runInfoResult != null){
                log.info("本次从缓存中命中结果,直接返回缓存的结果,报表code=[{}]", report.getCode());
                context.putInnerParam(KEY_IS_CACHE_RESULT, true);
                context.setResult(runInfoResult);
            } else{
                log.info("本次未从缓存中命中结果,将会到数据库中查询结果,报表code=[{}]", report.getCode());
            }
        } else {
            log.info("本次查询未开启缓存, 报表code=[{}]", report.getCode());
        }
    }

    @Override
    public void postInternal(ReportQueryContext context) {
        // 设置缓存
        ReportSql reportSql = context.getReportSql();
        // 如果不是缓存中得到的结果, 则需要设置到缓存中去
        RunInfoResult<ReportDataVO> result = context.getResult();
        if (result != null && result.isSuccess()){
            int expire = getCacheExpire(context,reportSql);
            if (expire > 0){
                String cacheKey = context.getInnerParam(KEY_RESULT_CACHE_KEY);
                cache.put(cacheKey, result, expire, TimeUnit.SECONDS);
            }

        }
    }

    /**
     * 返回缓存失效时间(默认5秒),如果为-1,则未开启缓存
     * @param reportSql
     * @return
     */
    private int getCacheExpire(ReportQueryContext context, ReportSql reportSql){
        boolean isFlowOff = context.getInnerParam(KEY_IS_FLOW_OFF);
        boolean isStaticReport = context.getInnerParam(KEY_IS_STATIC_REPORT);
        boolean isCacheResult = context.getInnerParam(KEY_IS_CACHE_RESULT);

        if(isFlowOff || isStaticReport){
            return getCachedTime(reportSql);
        }
        // 如果已经是缓存的结果, 直接返回-1
        if(isCacheResult){
            return -1;
        }
        Integer cached = reportSql.getCached();
        if (cached == null || cached == 0){
            return -1;
        }
        return getCachedTime(reportSql);
    }

    private int getCachedTime(ReportSql reportSql){
        Integer cachedTime = reportSql.getCachedTime();
        return cachedTime == null ? 5 : cachedTime;
    }


    private String createCacheKey(String reportCode,String sql, Map<String,Object> params){
        return  MD5Util.getMd5(String.format("%s--%s-%s", reportCode, sql, JSON.toJSONString(params)));
    }


}
